package cate.game.role.friend;

import cate.common.game.GameCollection;
import cate.common.table.d.GDFriend;
import cate.common.util.GameResult;
import cate.game.friend.po.FriendBox;
import cate.game.res.MixRes;
import cate.game.res.Pair;
import cate.game.role.Role;
import cate.game.role.RolePart;
import cate.game.role.RoleSimpleBase;
import cate.game.role.RoleSnapshot;
import cate.game.role.bag.hero.Hero;
import cate.game.role.base.RoleBase;
import cate.game.role.friend.aid.FriendAid;
import cate.game.role.friend.msg.*;
import cate.game.util.IntervalCounter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.*;
import java.util.stream.Collectors;

@Document(collection = GameCollection.NAME_ROLE_FRIEND)
public class RoleFriend extends RolePart {
    @NoteField(value = "好友列表")
    public List<Friend> list;

    @NoteField(value = "添加好友的申请列表")
    public FriendApply apply;

    @NoteField(value = "收到的好友赠礼列表")
    public FriendGift gift;

    @NoteField(value = "黑名单")
    public BlackList blackList;

    @NoteField(value = "援助")
    public FriendAid aid;

    public RoleFriend() {
    }

    public RoleFriend(Role role) {
        super(role);
    }

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (list == null) {
            list = new ArrayList<>();
        }
        if (apply == null) {
            apply = new FriendApply();
        }
        if (gift == null) {
            gift = new FriendGift();
        }
        if (blackList == null) {
            blackList = new BlackList();
        }
        for (Friend item : list) {
            item.initialize();
        }
        apply.initialize(role);
        gift.initialize(role);
        blackList.initialize(role);

        if (aid == null) {
            aid = new FriendAid();
        }
        aid.initialize(role);
    }

    @Override
    public void onEnter(){
        checkFriends();
        apply.checkBlackList(blackList.getBlackList());
    }

    /**
     * 查收邮件
     */
    public void checkFriends() {
        FriendBox box = role.getGame().friend.getBox(this.role.getUid(), false);
        if (box != null) {
            box.addToRole(this.role);
        }
    }

    public void update() {
        RoleSnapshot roleSnapshot = null;
        try{
            for (Friend f : list) {
                Role m = role.getGame().role.findOnline(f.it.uid);
                if (m != null) {
                    f.it.read(m);
                    if (roleSnapshot == null) {
                        roleSnapshot = new RoleSnapshot(role);
                    }
                    m.sendNow(new FriendUpdateResp(roleSnapshot));
                } else {
                    f.it.online = false;
                    Pair<RoleBase, Long> roleBasePair = role.getGame().role.getRoleBase(f.it.uid);
                    if (roleBasePair != null) {
                        f.it.base = new RoleSimpleBase().read(roleBasePair.k);
                        f.it.activeTime = roleBasePair.v;
                    }
                }
            }
        }catch(Exception e){
            logger.error("更新好友信息异常, e=",e);
        }
    }

    private void updateOnline() {
        for (Friend f : list) {
            if(f.it.base != null){
                Role m = role.getGame().role.findOnline(f.it.uid);
                if (m != null) {
                    boolean change = !f.it.online
                            || f.it.base.level != m.getBase().level
                            || f.it.base.power != m.getBase().power
                            || !StringUtils.equals(f.it.base.name, m.getBase().name);
                    if(change){
                        role.sendNow(new FriendUpdateResp(f.it.read(m)));
                    }
                }
            }
        }
    }

    /**
     * 查找好友
     *
     * @param name         好友姓名
     * @param excludeIdHex 排除的好友id
     */
    public void search(String name, String excludeIdHex) {
        List<RoleSnapshot> list = new ArrayList<>();
        // 先从active区快速查找
        role.getGame().role.activeForEach(
                (role -> {
                    if (role.getBase().name.contains(name)
                            && !StringUtils.equals(role.getUid(), excludeIdHex)) {
                        list.add(new RoleSnapshot(role));
                    }
                    return list.size() >= GDFriend.SEARCH_MAX;
                }),
                true);
        if (list.size() >= GDFriend.SEARCH_MAX) {
            return;
        }
        // 然后再从数据库查找
        List<Role> masters = role.getGame().role.getRolesByName(name, GDFriend.SEARCH_MAX);
        for (Role role : masters) {
            if (StringUtils.equals(role.getUid(), excludeIdHex)) {
                continue;
            }
            boolean includeInOnline = list.stream().anyMatch(e -> StringUtils.equals(e.uid, role.getUid()));
            if (includeInOnline) {
                continue;
            }
            list.add(new RoleSnapshot(role));
        }
        role.sendNow(new FriendSearchResp(list));
    }

    /**
     * 删除好友
     *
     * @param itIdHex 好友唯一id
     * @return 被删除的好友数据
     */
    public Friend delete(String itIdHex, boolean addBlackList) {
        try{
            if (addBlackList) {
                apply.removeSomeOne(itIdHex);
            }
            Iterator<Friend> iterator = list.iterator();
            while (iterator.hasNext()) {
                Friend old = iterator.next();
                if (StringUtils.equals(old.it.uid, itIdHex)) {
                    iterator.remove();
                    if (role.getStatus().isOnline()) {
                        role.sendNow(new FriendDeleteResp(itIdHex));
                    }
                    return old;
                }
            }
        }catch(Exception e){
            logger.error("删除好友异常,e=",e);
        }
        return null;
    }

    /**
     * 删除好友逻辑
     *
     * @param tarIdHex 好友唯一id
     */
    public void doDelete(String tarIdHex, boolean addBlackList) {
        Friend f = delete(tarIdHex, addBlackList);
        if (f == null) {
            logger.error("好友删除f==null, tarIdHex={}", tarIdHex);
            return;
        }
        if (f.it == null) {
            logger.error("好友删除f.it == null, tarIdHex={}", tarIdHex);
            return;
        }
        if (addBlackList) {
            role.getGame().friend.deleted(tarIdHex, role.getUid(), addBlackList);
        }
    }

    /**
     * 添加好友
     */
    public void add(RoleSnapshot applicant) {
        if (StringUtils.equals(applicant.uid, role.getUid())) {
            role.getGame().notice.message(role,"无法添加自己为好友");
            return;
        }
        boolean existed = false;
        for (Friend old : list) {
            if (StringUtils.equals(old.it.uid, applicant.uid)) {
                existed = true;
                break;
            }
        }
        if (!existed) {
            Friend friend = Friend.create(role.getUid(), applicant, role.getGame());
            synchronized (this) {
                list.add(friend);
            }
            if (role.getStatus().isOnline()) {
                role.sendNow(new FriendAddResp(friend));
            }
        }
        apply.removeIfFriend(applicant.uid);
        role.getHistory().action.friendNum(list.size());
        role.getGame().notice.message(role, applicant.base.name + "与您成为了好友");
    }

    /**
     * 赠送好友礼物
     *
     * @param uid 好友的唯一id
     */
    public void giftFor(String uid) {
        List<Friend> l = Lists.newArrayList();
        if (StringUtils.isNotBlank(uid)) {
            Friend f = getFriend(uid);
            if (f == null) {
                role.getGame().notice.message(role,"好友不存在");
                return;
            }
            l.add(f);
        } else {
            l.addAll(list);
        }
        if (l.isEmpty()) {
            role.getGame().notice.message(role, "没有好友可以赠送");
            return;
        }
        EcResult<MixRes> r = giftFor(l);
        if (!r.ok()) {
            role.getGame().notice.message(role, r.message);
            return;
        }
        role.getGame().notice.message(role, "赠送礼物成功");
    }

    /**
     * 赠送好友礼物
     *
     * @param friends 好友们
     */
    private EcResult<MixRes> giftFor(List<Friend> friends) {
        GameResult<MixRes> r = new GameResult<>();
        int leftSendTime = GDFriend.GIFT_DAY_MAX - gift.sentToday;
        if (leftSendTime <= 0) {
            return r.fail("今日赠送次数已用完");
        }
        List<Friend> targets = new ArrayList<>();
        if (friends.size() > 1) {
            // 一键赠送
            List<Friend> giftableList = new ArrayList<>();
            for (Friend friend : friends) {
                if (!gift.getSentToday(friend.it.uid)) {
                    giftableList.add(friend);
                }
            }
            giftableList.sort(new Friend.Sort());
            final int num = Math.min(giftableList.size(), leftSendTime);
            for (int i = 0; i < num; i++) {
                Friend f = giftableList.get(i);
                targets.add(f);
            }
        } else {
            // 指定赠送
            Friend f = friends.get(0);
            if (gift.getSentToday(f.it.uid)) {
                return r.fail("每天只能赠送好友一次礼物");
            }
            targets.add(f);
        }
        if (targets.isEmpty()) {
            return r.fail("没有可以被赠送礼物的好友啦");
        }
        r.data = gift.giftFor(targets);
        role.getHistory().action.friendGiftSentTimes(targets.size());
        role.getTrace().giveFriendGift(targets.size());
        return r.success();
    }

    /**
     * 测试任务使用
     *
     * @param num :次数
     */
    public void testTask(int num) {
        role.getHistory().action.friendGiftSentTimes(num);
        role.getGame().notice.message(role,String.format("赠送好友点%d次",num));
    }


    /**
     * 根据唯一ID获取好友
     *
     * @param uid 好友唯一id
     * @return 好友数据
     */
    @JsonIgnore
    public synchronized Friend getFriend(String uid) {
        for (Friend f : list) {
            if (StringUtils.equals(f.it.uid, uid)) {
                return f;
            }
        }
        return null;
    }

    /**
     * 处理礼物（即接收礼物）
     *
     * @param uid 好友唯一id
     */
    public void recvGift(String uid) {
        GameResult<MixRes> r = gift.recvGift(uid);
        if (!r.ok()) {
            role.getGame().notice.message(role, r);
            return;
        }
        role.getGame().notice.dialogReward(role, r.data);
        role.getGame().notice.message(role, "成功接受礼物");
        if (StringUtils.isNotBlank(uid)) {
            apply.handleApply(uid, true);
        }
    }

    /**
     * 处理好友申请
     *
     * @param applicantIdHex 申请的唯一id
     * @param ok             是否同意
     */
    public void handle(String applicantIdHex, boolean ok) {
        if (ok && isFull()) {
            role.getGame().notice.message(role, "您的好友列表已满，无法添加更多好友");
            return;
        }
        if (applicantIdHex != null && inBlackList(applicantIdHex)) {
            role.getGame().notice.message(role, "您已经将其拉进黑名单，请删除黑名单后再试");
            return;
        }
        apply.checkBlackList(blackList.getBlackList());
        apply.handleApply(applicantIdHex, ok);
    }

    @JsonIgnore
    private int getFriendNum() {
        return this.list.size();
    }

    @JsonIgnore
    public boolean isFull() {
        return getFriendNum() >= GDFriend.FRIEND_MAX;
    }

    /**
     * 申请添加好友
     *
     * @param targetIdHex 目标唯一id
     */
    public void applyFor(String targetIdHex) {
        if (isFull()) {
            role.getGame().notice.message(role, "当前好友已达上限， 请清理后在添加好友!");
            return;
        }
        if (StringUtils.equals(targetIdHex, role.getUid())) {
            role.getGame().notice.message(role, "不能添加自己为好友");
            return;
        }
        if (list.stream().anyMatch(e -> e.it.uid.equals(targetIdHex))) {
            role.getGame().notice.message(role, "该玩家已经是您的好友了");
            return;
        }
        if (inBlackList(targetIdHex)) {
            role.getGame().notice.message(role, "您已经将其拉黑，无法添加其为好友");
            return;
        }
        FriendApplyItem applyItem = new FriendApplyItem();
        applyItem.applicant = new RoleSnapshot(role);
        applyItem.targetIdHex = targetIdHex;
        applyItem.time = System.currentTimeMillis();

        Role friend = role.getGame().role.getRole(applyItem.targetIdHex);
        if (friend != null) {
            GameResult<Void> r = role.getGame().friend.recvApply(friend, applyItem);
            if (!r.ok()) {
                role.getGame().notice.message(role, r.message);
                return;
            }
            RoleSnapshot rs = new RoleSnapshot(friend);
            add(rs);
            role.sendNow(new FriendUpdateResp(rs));
        }
    }

    /**
     * 收到好友申请申请
     *
     * @param item 好友添加申请
     */
    public GameResult<Void> recv(FriendApplyItem item) {
        GameResult<Void> r = new GameResult<>();
        if (getFriend(item.applicant.uid) != null) {
            return r;
        }
        return apply.recv(item);
    }

    /**
     * 收到好友的礼物
     *
     * @param item 来自好友的赠礼
     */
    public void recv(FriendGiftItem item) {
        recv(item, true);
    }

    public void recv(FriendGiftItem item, boolean notice) {
        gift.recv(item, notice);
    }

    /**
     * 添加到黑名单
     *
     * @param uid 唯一id
     */
    public void addBlackList(String uid) {
        Role black = role.getGame().role.getRole(uid);
        if (black == null) {
            role.getGame().notice.message(role, "该玩家不存在");
            return;
        }
        if (StringUtils.equals(uid, role.getUid())) {
            role.getGame().notice.message(role, "不能把自己加入黑名单");
            return;
        }
        GameResult<Void> r = blackList.addCheck(black);
        if (!r.ok()) {
            role.getGame().notice.message(role, r);
            return;
        }
        blackList.add(black);
        doDelete(uid, true);
        role.getGame().notice.message(role, "添加黑名单成功");
    }

    public void removeBlackList(String uid) {
        blackList.remove(uid);
    }

    public boolean inBlackList(String uid) {
        return blackList.existed(uid);
    }

    @Override
    protected void offline() {
        updateSelfAsFriend();
    }

    private void updateSelfAsFriend() {
        RoleSnapshot rs = null;
        for (Friend f : list) {
            if (f.it != null) {
                Role m = role.getGame().role.findOnline(f.it.uid);
                if (m != null) {
                    if (rs == null) {
                        rs = new RoleSnapshot(role);
                        rs.online = false;
                        rs.activeTime = System.currentTimeMillis();
                    }
                    m.sendNow(new FriendUpdateResp(rs));
                }
            }
        }
    }

    public void getAidList(int funcId) {
        aid.getAidList(funcId, findActiveFriends());
    }

    private Set<String> findActiveFriends(){
        return list.stream().filter(e -> !e.notActive()).map(e -> e.it.uid).collect(Collectors.toSet());
    }

    public GameResult<Void> provideAidHero(int funcId, String heroUid) {
        GameResult<Void> r = new GameResult<>();
        Hero hero = role.getBag().hero.getItem(heroUid);
        if (hero == null) {
            return r.fail("英雄不存在");
        }
        return aid.provide(funcId, hero);
    }

    public GameResult<Void> borrowAidHero(int funcId, String roleUid, String heroUid) {
        return aid.borrow(funcId, roleUid, heroUid);
    }

    public Hero findAidHero(int funcId, String heroUid) {
        return aid.findAidHero(funcId, heroUid);
    }

    @Override
    public void onDaySpan(boolean silence) {
        apply.onDaySpan(silence);
        gift.onDaySpan(silence);
        aid.onDaySpanSafe(silence);
    }

    @Transient
    private final IntervalCounter synTrigger = new IntervalCounter(200);
    public void tick(){
        //不走同时更新逻辑
        if(synTrigger.trigger()){
            try{
                updateOnline();
            }catch (Exception e){
                logger.error("{}定时更新好友失败", role.getBase().name);
            }
        }
    }
}
